home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Precision Software Appli…tions Silver Collection 1
/
Precision Software Applications Silver Collection Volume One (PSM) (1993).iso
/
demos
/
devel3.exe
/
DEVEL3.DOC
< prev
next >
Wrap
Text File
|
1992-06-08
|
43KB
|
968 lines
REND386 -- A 3-D Polygon Rendering Package for the 386 and 486
Written by Dave Stampe and Bernie Roehl
LIBRARY Documentation
Version 3.01 - June 1992
This package contains a library of routines (callable from C) that will
render 3 dimensional scenes on 386 and 486 based systems with a VGA display.
The package also includes a simple user-interface library with joystick
support, and a demo program to show what it's capabable of.
The package is designed to be fast. It accomplishes that goal by making
use of the special instructions that exist only on 386 and 486 processors;
it will not run on 286s, 8086s or 8088s.
It also requires Turbo C++ or Borland C to link; Turbo C 2.00 seems to
generate unresolved references.
A NOTE ON SPEED:
Speed is not a straightforward thing to measure. There is a relationship
between the speed of the processor, the complexity of the scene, and the
number of frames per second.
With this software, a 512-polygon scene can be rendered at speeds up to
14 frames/second on a 486/25; this corresponds to a speed of over 7000
polys/second.
LICENSING:
These libraries may be freely used to write software for release into the
public domain. Permission to use these libraries for the production of
commercial software (including shareware) must be obtained from the authors,
Bernie Roehl (broehl@sunee.uwaterloo.ca) and Dave Stampe
(dstampe@sunee.uwaterloo.ca).
Licensing terms will be very, very reasonable.
SOFTWARE DOCUMENTATION
The following sessions describe the various data structures and routines
that comprise the rend386 package.
SECTION A -- OBJECTS
The following functions deal with OBJECTs. OBJECTs have several properties
associated with them: a set of vertices, a set of polygons, a name, an
owner and a set of flags. Each vertex has an x,y,z location in the object's
native coordinates, and a (probably different) x,y,z location in world
coordinates. Each polygon has a color value and an array of pointers to the
vertices that define that polygon.
OBJECT *new_obj(int type, int nv, int np, char *name)
Creates a new object, with room for up to nv vertices and np polygons.
The given name is set in the object.
void add_vertex(OBJECT *obj, long x, long y, long z)
Adds a vertex to the given object, with x,y,z as its coordinates
in object space.
POLY *add_poly(OBJECT *obj, unsigned color, int npoints)
Adds a polygon to the given object, with the given color and room
for up to npoints vertices.
void add_point(OBJECT *obj, POLY *p, int vertnum)
Adds a point to a polygon. Vertex number vertnum in object obj is
added to the given polygon. The vertices must be added in a
"clockwise" order as seen from outside the object.
void delete_obj(OBJECT *obj)
Deletes the given object and frees all memory associated with it.
long object_bounds(OBJECT *obj, long *x, long *y, long *z)
Obtains the x,y,z extents of the given object.
void compute_obj(OBJECT *obj)
Does internal computation of polygon normals, bounding sphere, and
so forth. Must be called once the object is complete (i.e. all the
vertices and polygons have been added, and the points comprising
the polys have been defined) and before the object is rendered.
unsigned get_object_sorting(OBJECT *ojb)
Returns the current value of the object's depth sorting type field.
Values are:
DEEPEST - the default; sorts by deepest (i.e. farthest) vertex
AVERAGE - use average depth rather than maximum
ATBACK - sorts the polys as if they were very far away
You can also depth sort by object rather than by poly by OR'ing in
the BYOBJECT value. In addition, you can sort by the object's
clipping sphere by OR'ing in the USE_CENTER value.
void set_object_sorting(OBJECT *obj, unsigned depth_type)
Sets an object's depth sorting type field.
void set_obj_flags(OBJECT *obj, unsigned char value)
Sets the object's flags.
unsigned char get_obj_flags(OBJECT *obj)
Gets the object flags.
The bottom two bits of the flags word are named OBJ_INVIS (which indicates
that the object is invisible and should not be drawn) and OBJ_HIGHLIGHTED
(indicating that the object should be drawn highlighted).
void get_obj_info(OBJECT *obj, int *nv, int *np, char *buffer, int maxn)
Extracts the number of vertices in obj and stores it in *nv, and
similarly stores the number of polys in obj in *np. If maxn is
non-zero, the given buffer is filled in with the name of the object
(if one has been set). The buffer must contain room for up to maxn
characters.
void get_vertex_info(OBJECT *obj, int vertnum, long *x, long *y, long *z)
Extracts the x, y and z values of vertex number vertnum in obj.
This function obtains the coordinates in the object coordinate system.
void get_vertex_world_info(OBJECT *obj, int vertnum, long *x, long *y, long *z)
Extracts the x, y and z values of vertex number vertnum in obj.
This function obtains the coordinates in the world coordinate system.
void get_poly_info(OBJECT *obj, int polynum, unsigned *color, int *nverts,
int *verts, int maxverts)
Extracts information from polygon number polynum in obj. The color
parameter is set to the polygon color (see colors.doc for details).
If maxverts is non-zero, then the array of integers verts is filled
in with the index numbers of the vertices comprising the polygon.
No more than maxverts vertices will be stored; if there are more
than that number of vertices in the object, the rest will be ignored.
void set_poly_color(OBJECT *obj, int polynum, unsigned color)
Sets the color of polygon number polynum in obj to the given color.
See colors.doc for details of how the color parameter is interpreted.
void *get_object_owner(OBJECT *obj)
Returns the owner field of the given object. The owner field is not
used by the renderer, and is provided for the convenience of the
application programmer. It might, for example, be a pointer to the
segment that this object is the representation of (see the section on
segments for more information).
void set_object_owner(OBJECT *obj, void *owner)
Sets the object's owner field.
OBJECT *copy_obj(OBJECT *obj, int nverts, int npolys, char *name)
Makes a copy of the given object, with room for nverts vertices and
npolys polygons. This is to allow you to add additional vertices
and polygons using an existing object as a starting point. The name
field is set in the copy.
void copy_world_to_object(OBJECT *obj)
Copies the world coordinates of the object into its object coordinates,
effectively making the object's coordinate system the same as the
world coordinate system. Rarely used.
void highlight_obj(OBJECT *obj)
Turns on the HIGHLIGHT bit in the color field of all the polys in the
given object.
void unhighlight_obj(OBJECT *obj)
Turns off the HIGHLIGHT bit in the color field of all the polys in the
given object.
SECTION B -- OBJLISTS
The following functions deal with object lists, or OBJLISTs. An OBJLIST is
what you pass to the renderer (i.e. you tell it to render a given list of
objects).
OBJLIST *new_objlist()
Creates a new object list and returns a pointer to it.
void add_to_objlist(OBJLIST *list, OBJECT *obj)
Adds an object to an object list.
void remove_from_objlist(OBJLIST *list, OBJECT *obj)
Removes an object from an object list.
void del_objlist(OBJLIST *list)
Deletes an object list.
OBJECT *first_in_objlist(OBJECT **objlist)
Returns a pointer to the first object in a list.
OBJECT *next_in_objlist(OBJECT **objlist, OBJECT *obj)
Returns a pointer to the next object (after obj) in a list.
OBJECT *prev_in_objlist(OBJECT **objlist, OBJECT *obj)
Returns a pointer to the previous object (after obj) in a list.
int is_first_in_objlist(OBJECT **objlist, OBJECT *obj)
Returns non-zero if obj is the first entry in the given list.
int is_last_in_objlist(OBJECT **objlist, OBJECT *obj)
Returns non-zero if obj is the last entry in the given list.
SECTION C -- VIEWS
In addition to an OBJLIST, the rendering routines need to know about your
current viewpoint; the VIEW structure contains this information. It has
three fields (ex, ey, ez) that define the current location of your "eye"
or "camera" in world coordinates, and another three (pan, tilt and roll)
that define the "camera's" rotation about the Y (vertical) axis, X
(horizontal) axis, and Z (forward) axis respectively.
The VIEW also has near/far clipping information in the the hither and yon
fields (in world coordinates).
(A note about coordinates: X is positive to the right, Y is positive up and
Z is positive away from you).
There is also a zoom factor, which works like the zoom on a camera.
The angles and zoom factor are all stored as 32-bit (long) integers, but
are best thought of as floating point numbers multiplied by 65536L. This
is referred to in this document as "16.16" format (16 bits of integer,
16 bits of fraction).
A VIEW also has information about what area on the screen should be used to
present the scene; these left, right, top and bottom values are in absolute
screen coordinates. There is also an "aspect ratio" which determines the
relative size of a non-square pixel.
The world-space coordinates of a single light source are stored in the lx,ly
an lz fields.
There is also a "flags" field whose bottom bit is set if the objects should
all be drawn in wire-frame mode rather than as filled polygons. Because no
explicit edge information is stored in the OBJECT data structures, wireframing
is actually slower than filled-polys since each edges winds up getting drawn
twice.
The next two bits (called HIDE_HIGHLIGHTED and HIDE_UNHIGHLIGHTED) determine
whether highlighted (and/or unhighlighted) objects should be invisible.
There is now a pair of values (x_offset and yoffset) that effectively shift
the screen center by a given amount (number of pixels), and an orientation
field whose bottom bit (if set) indicates we should flip around the X axis,
and whose next higher bit (if set) indicates we should flip around the Y
axis. There's also a viewpoint transform matrix.
Finally, there's a 200 byte "work area" associated with each VIEW.
void compute_view_factors(VIEW *v)
This function has been replaced by initialize_screen_factors()
and fast_view_factors(), but can still be used since it just calls
those two routines.
void initialize_screen_factors(VIEW *v)
Computes the screen factors and sets them into the given VIEW struct.
Should be called once initially, and again whenever the the zoom,
x_offsets, y_offset or orientation have changed.
void fast_view_factors(VIEW *v)
Should be called whenever the viewpoint changes.
In addition to all this, there's a global struct called Screeninfo that
has the minimum and maximum X and Y values, the number of available colors,
the number of pages, and the screen coordinates of the screen "center".
SECTION D -- MATRICES
A matrix is a 4 by 3 array of long (32-bit) integers. It can best be thought
of as an upper 3x3 matrix representing rotations, and 3-element bottom row
vector representing translation. A matrix is used to transform a point or
an object.
void make_matrix(MATRIX m, long rx, long ry, long rz, long tx, long ty, long tz)
Fills in a matrix with the appropriate values for rotation and translation.
The rx, ry and rz values are the rotations around the three axes; these
values are all in 16.16 format (i.e. a 16-bit integer part and a 16-bit
fractional part) and are expressed in degrees. To convert from degrees in
floating-point to this format, just multiply by 65536L. The translation
values tx, ty and tz are all in 32-bit "world" units.
void matrix_mult(MATRIX a, MATRIX b, MATRIX c)
Does a pseudo-matrix-multiply. The 'a' matrix is multiplied by the 'b'
matrix and the result is stored in the 'c' matrix. Note that this is not
a real matrix multiply; only the top 3x3 matrix is affected. You must deal
with the bottom 3-element vector separately.
void matrix_point(MATRIX m, long *xp, long *yp, long *zp)
Applies the given matrix to a point given the addresses of its x,y,z values.
void apply_matrix(OBJECT *obj, MATRIX m)
Applies the given matrix to all the vertices in the given object,
generating new world coordinates from the object coordinates.
void matrix_transpose(MATRIX a, MATRIX b)
Transposes the matrix 'a' into the matrix 'b' (exchanges rows with columns).
void inverse_matrix(MATRIX a, MATRIX b)
Finds the pseudo-inverse of 'a' and stores it in 'b'. This is not a real
inverse; it only works for matrices in which the top 3x3 is a rotation
submatrix and the bottom row is a translation vector.
void identity(MATRIX m)
Sets the given matrix up as the identity matrix.
void matrix_copy(MATRIX src, MATRIX dst)
Copies the given matrix src to the given matrix dst.
void matrix_rot_copy(MATRIX src, MATRIX dst)
Similar to matrix_copy, but zeroes out the last row of the result.
Probably does not need to be called directly.
long m_mult(long a, long b)
Multiples two matrix elements together and returns the result. Probably
does not need to be called directly.
long isine(long angle)
Returns the sine of the angle in internal format; the angle is in degrees,
in 16.16 format. Probably does not need to be called directly.
long icosine(long angle)
Returns the cosine of the angle in internal format; the angle is in degrees,
in 16.16 format. Probably does not need to be called directly.
SECTION E -- SEGMENTS
A segment is a piece of an articulated (multi-jointed) figure. Each segment
has a parent segment; the parent the "root" segment is NULL. Each segment
has a linked list of children, and each segment has a 'sibling' which
is the next segment in the parent's linked list of children.
Each segment also has several values associated with it, including the
position and orientation of the segment relative to its parent (or relative
to the world coordinate system, for the root object) as well as a name and
a "representation" which might, for example, be a pointer to an OBJECT.
SEGMENT *new_seg(SEGMENT *parent)
Creates a new segment, with the given segment as its parent. If the
'parent' parameter is NULL, this is a root object.
void seg_setrep(SEGMENT *s, void *rep)
Sets the 'representation' field of the given segment to the given value.
void *seg_getrep(SEGMENT *s)
Returns the value of the 'representation' field of the given segment.
void seg_getposition(SEGMENT *s, long *x, long *y, long *z, long *rx,
long *ry, long *rz)
Stores the current position of the given segment (relative to its parent)
in *x, *y and *z; also stores its orientation (in 16.16 format) in *rx,
*ry and *rz.
char *seg_getname(SEGMENT *s)
Returns the name of the given segment.
void set_setname(SEGMENT *s, char *name)
Sets the name of the given segment to the given value; a copy is made.
void rel_move_segment(SEGMENT *obj, long x, long y, long z)
Performs a relative move of the given segment; x, y and z are the amounts
to move the segment along each of the three axes. After calling this
routine, you must at some point call update_segment().
void abs_move_segment(SEGMENT *obj, long x, long y, long z)
Performs an absolute move of the given segment; x, y and z are the new
to move the segment along each of the three axes. After calling this
routine, you must at some point call update_segment().
void rel_rot_segment(SEGMENT *seg, long rx, long ry, long rz)
Performs a relative rotation of the given segment; x, y and z are the
angles to rotate the segment along each of the three axes (in 16.16
format). After calling this routine, you must at some point call
update_segment().
void abs_rot_segment(SEGMENT *seg, long rx, long ry, long rz)
Performs an absolute rotation of the given segment; x, y and z are the
angles to rotate the segment to along each of the three axes (in 16.16
format). After calling this routine, you must at some point call
update_segment().
void update_segment(SEGMENT *s)
Takes the current position and orientation and uses it (and similar
information from the parent segment, and its parents, and so on) to
transform the segment's representation.
void full_update_segment(SEGMENT *s)
Similar to update_segment, but explicitly recomputes the matrices
at each node even if it's not needed. You should not need to use
this function.
SEGMENT *find_root_segment(SEGMENT *s)
Walks up the tree from the given segment and finds the root segment for
the figure of which the segment is a part.
SEGMENT *parent_segment(SEGMENT *s)
Returns the parent of the given segment.
SEGMENT *child_segment(SEGMENT *s)
Returns the child of the given segment.
SEGMENT *sibling_segment(SEGMENT *s)
Returns the sibling of the given segment.
SEGMENT *copy_segment(SEGMENT *s, long dx, long dy, long dz,
void *(*copyrep_fn)())
Makes a copy of the given segment, displaced from the original by
dx,dy,dz, and uses the given function (if not NULL) to make a copy
of the original segment's representation and set it as the
representation of the copy. A pointer to the new segment is returned.
void delete_segment(SEGMENT *s, void (*delrep_fn)())
Deletes the given segment and frees the memory associated with it; calls
the given function (if not NULL) to delete any representation associated
with the given segment.
void attach_segment(SEGMENT *s, SEGMENT *to)
Attaches the given segment to the given parent ('to'), making it a child
of that segment.
void detach_segment(SEGMENT *s)
Detaches the given segment from its parent, and makes it an independent
segment with no parent. Its children remain attached to it, and other
siblings are unaffected (i.e. they still descend from the same parent
that has 'disowned' the given segment).
void seg_set_load_info(SEGMENT *s, char *filename, float sx, float sy,
float sz, long tx, long ty, long tz)
Stores the information about the scaling and translation of a loaded
representation into the SEGMENT struct; this is so we can get it back
later when it comes time to write the segment information back out to
a file again.
char *seg_get_load_info(SEGMENT *s, float *sx, float *sy, float *sz, long *tx,
long *ty, long *tz)
Retrieves the scaling and translation of a loaded representation from the
SEGMENT struct, so we can write it out to a file.
unsigned get_seg_limits(SEGMENT *s, long lims[12])
Retrieves the current segment limits and stores them in the 12 elements
of lims[]. The first two values are the minimum and maximum X
translation values, the next two are the minimum and maximum Y
translation values, and the next two are the minimum and maximum
Z translation values. The last six are the corresponding rotational
limits (minrx, maxrx...) multiplied by 65536L. The return value of
the function is a bitmask, the low-order bit indicating that the
minimum X value is relevant, the next higher bit indicating that
the maximum X value is relevant, and so on. The file rend386.h
contains values like LIM_MINTX and LIM_MAXTY, so you don't have
to figure out the bits by hand.
unsigned set_seg_limits(SEGMENT *s, long lims[12], unsigned which)
Sets the current segment limits from the 12 values in lims[]. The
values are as for get_seg_limits(). The "which" parameter is a
bitmask indicating which values are to be altered by the function.
The function returns the new (updated) bitmask indicating which
values have now been set.
MATRIX *get_seg_matrix(SEGMENT *s)
Returns a pointer to the matrix associated with the given segment.
SEGMENT *find_segment_by_name(SEGMENT *s, char *name)
Returns a pointer to the segment (descended from the given segment)
whose name matches the given name. If none is found, NULL is returned.
SECTION F -- COLORS
Four integers are defined in color16.c or color256.c; they are as follows:
screen_clear_color -- the color to which the screen should be cleared
wireframe_color -- the color in which to draw wireframe outlines
highlight_color -- the color in which to highlight (outline) objects
highest_color -- the highest color available in this mode (15 or 255)
They can be changed in the appropriate colors file to whatever values you
choose.
Color-related routines are as follows:
set_colors()
Sets up (initializes) the palette.
reset_colors()
Does nothing, but could eventually restore old palette.
void read_palette(char *buffer)
Stores the current palette into the given buffer; the buffer must have
room for either 16 or 256 three-byte entries.
int user_poly_color(POLY *p, int pcolor, long maxz)
Does the mapping of user-specified color values into whatever format
gets passed to lower-level drawing routines; this is where cosine
lighting is done, for example. The maxz specifies the polygon depth
(so you can do distance-based shading).
These routines may be changed at will, but bear in mind that this may make
color usage incompatable with data files generated elsewhere.
SECTION G -- RENDERING ROUTINES
These routines are the main interface to the renderer.
void setup_render()
Sets up the internal data structures used by the renderer; should be
called before doing any other calls to any of the rend386 library
routines.
void reset_render()
Frees memory allocated for internal data structures used by the renderer;
should be called before exiting.
void render(OBJLIST *objlist, VIEW *view)
Renders all the objects in the given objlist from the given viewpoint,
writing to the current page (see set_drawpage()).
This next routine can be called by the user part of the renderer code.
int poly_cosine(void *poly)
Given a pointer to a polygon, returns the cosine of the angle between
the current light source and the polygon's surface normal; the value
returned is actually the cosine times 128.
SECTION H -- ROUTINES CALLED BY THE RENDERER
These routines are provided by the user (source in render.c); you can implement
your own "back end" to the renderer just by rewriting these routines.
void user_setup_blitter()
Sets up the blitter.
void user_reset_blitter()
Resets the blitter.
void user_box(int x1, int y1, int x2, int y2, int color)
Draws a box with top left corner (x1,y1) and bottom right corner (x2,y2)
filled in the given color.
void user_text(int x, int y, int color, char *string)
Displays a string at (x,y) in the given color.
void user_render_poly(int number, int *coords, int color, long MAXZ)
Similar to ppoly, but at a higher level; lets you intercept calls and
add features. See render.c for details. The maxz values species the
polygon depth, so you can make distance-based polygon rendering
decisions. NOTE THAT THIS FUNCTION HAS CHANGED ITS DEFINITION SINCE
VERSION 2.02 OF THIS SPECIFICATION!!! We apologize for any
inconvenience.
There is also a global flag, an integer called 'wireframe', which, if non-zero,
will cause all objects to be rendered in wireframe mode. Note that this is
in general slower than actual filled-polygon rendering, since the absence of
explicit edge information forces each edge to be drawn twice.
SECTION I -- MISCELLANEOUS ROUTINES
OBJECT *where_screen_pt(int *pol, int *vert, int x, int y)
Given the x, y location of a point on the screen this routine returns a
pointer to the object that point is on; if pol is not NULL, it is
set to be the index in the object of the polygon the cursor is on, and
if vert is not NULL, it is set to the index of the vertex in the object
that is nearest to the given point.
long where_pt(OBJLIST objlist, long x, long y, long z, OBJECT **obj, int *vert)
Finds which object (if any) the point (x,y,z) is contained within, and
sets *obj to point to that object. Also sets *vert to be the index of
the vertex on that object which is closest to (x,y,z) and returns the
distance from (x,y,z) to that vertex. THIS ROUTINE HAS NOT BEEN
EXTENSIVELY TESTED! Caveat programmer.
SECTION J -- LOWER-LEVEL GRAPHICS ROUTINES
enter_graphics()
Enter graphics mode.
exit_graphics()
Exit graphics mode.
void clear_display(int page)
Clear the given display page.
int set_drawpage(int page)
Make the given page the 'active' one for drawing.
int set_vidpage(int page, int wait)
Make the given page the 'visible' one.
void vgabox(int left, int top, int right, int bottom, int color)
Draw a box on the display at the given location in the given color.
No clipping is done!
void vgaline(int left, int top, int right, int bottom, int color)
Draw a line on the display at the given location in the given color.
No clipping is done!
SECTION K -- MOUSE ROUTINES
These routines all go through the mouse driver for reading the mouse position,
but draw their own cursor (in order to support mode Y graphics).
int mouse_init()
Initializes the mouse, returns non-zero if one is present.
void mouse_deinit()
De-initializes the mouse.
int mouse_read(int *x, int *y, unsigned *buttons)
Reads the current screen position of the mouse into *x and *y, and the
current button state into *buttons; returns non-zero if the mouse or
its buttons have changed since the last call.
int mouse_hide()
Hides the mouse and decrements the mouse flag.
void mouse_show(int page)
Increments the mouse flag, and displays the cursor if the resulting
value is zero.
SECTION L -- JOYSTICK ROUTINES
The package provides support for reading a joystick. The following struct
describes the information associated with a joystick port:
typedef struct {
int x, y, buttons;
int cenx, ceny;
int xrange, yrange;
long scale;
int port; /* port number, 0 or 1; -1 means 'unused' */
} joystick_data;
The x, y and buttons fields are the values read from the joystick. The
cenx and ceny values are the values of x and y that are read when the joystick
is centered; the values the application program sees are relative to these
center values, so (0,0) is a centered stick.
The xrange and yrange values are the maximum values for x and y; the scale
factor is the ratio of movement on the screen to movement of the joystick.
The value the user sees after a call to joystick_read() is, in the case of
x, ((x_as_read - cenx) * scale)/xrange.
The port value specifies which of the two possible joysticks this structure
corresponds to.
int joystick_check()
Checks for the presence of one or two joysticks; the returned value has
the low-order bit set if joystick 1 is present, and the next higher bit
set if joystick 2 is present.
void joystick_init(joystick_data *joy, int port)
Initializes the joystick on the given port (0 or 1)
void joystick_setscale(joystick_data *joy, int value)
Sets the scale factor for the given joystick
void joystick_scale(joystick_data *joy, int dir)
Used for joystick calibration; should be called with dir = 0 and joystick
forward and to the left, then again with dir = 1 and joystick backward and
to the right. This sets the xrange and yrange values for you.
int joystick_read(joystick_data *joystick)
Reads the current values of the specified joystick.
void joystick_quit()
De-initializes joystick processing (current does nothing)
SECTION M -- POINTER ROUTINES
Primitive support is provided for 3-dimensional pointing devices. The file
pointer.c contains a set of routines that map the movement of a standard
mouse into three-space; horizontal is X, vertical is Y, and vertical with the
right-hand button down is Z. If you have a "real" 3D pointing device (e.g.
PowerGlove) you can rewrite the routines in pointer.c to make use of it.
The following structure is used to return information about the 3D pointer:
typedef struct {
long x, y, z; /* location in world coordinates */
long pan, tilt, roll; /* orientation around x, y, z */
unsigned buttons; /* 16 bits */
unsigned gesture; /* for future expansion */
int port; /* in case we need this */
long sx, sy, sz; /* scaling factors */
int flex[16]; /* up to 16 words of flexion information */
} POINTER;
The x, y and z values are the location of the pointer in world coordinates.
The pan, tilt and roll are the rotation of the pointer in 16.16 format.
The buttons represent up to 16 buttons, each of which is down or up; in the
case of the powerglove, this may correspond to one byte of finger information
and one byte of button information (for example).
The port value is used in case multiple 3D pointing devices are available.
The scaling values are used to alter the movement of the pointer relative
to that of the pointing device.
The 16 words of flexion information are provided for devices that provide
more detailed information about joint angles; the gesture field is provided
for those systems that support gesture recognition.
pointer_init(int port, POINTER *pointer)
Initializes the pointing device on the given port; returns non-zero if
a device is found, zero otherwise.
pointer_read(POINTER *pointer)
Reads the given pointer, updating the fields in the given struct, and
returning non-zero if the pointer has changed state (location,
orientation or buttons) since the last call to pointer_read().
void pointer_scale(POINTER *pointer, long sx, long sy, long sz)
Sets the scaling for the pointer x, y, and z values.
void pointer_quit(POINTER *pointer)
De-initializes the given pointing device.
void pointer_move(POINTER *pointer, long x, long y, long z)
Sets the current position of the pointer in world coordinates; all
subsequent movements of the pointing device will be relative to this
position.
SECTION N -- USER-INTERFACE ROUTINES
These routines allow you to provide a simple user interface.
void neatbox(int w, int h, int *x, int *y)
Displays a neat-looking box of height h and width w, centered on the
screen. The *x and *y values are set to the computed top-left corner
of the box.
void poptext(char *text[])
Pops up text box on the screen; the 'text' parameter is an array of
strings, the last of which is NULL.
void popmsg(char *msg)
Pops up a one-line message in a box centered on the screen
unsigned askfor(char *prompt, char *buff, int n)
Prompts the user to enter a string; buff must contain room for up to n
characters plus a null byte to terminate the string.
SECTION O -- PLG FILE I/O ROUTINES
The ".plg" file format is described in plg.doc; it is not tightly coupled to
the rest of the renderer software.
OBJECT *load_plg(FILE *in)
Loads a .plg file and returns a pointer to the newly-created OBJECT.
The global variable load_err will be set to a non-zero value if there
was a problem loading the file. As the file is loaded, it is first
scaled by the current scaling factor and then shifted by the current
x,y,z offset.
void set_loadplg_offset(long x, long y, long z)
Specifies the x,y,z offset to use; see figure.doc for details.
void set_loadplg_scale(float x, float y, float z)
Specifies the x,y,z scaling to use; see figure.doc for details.
save_plg(OBJECT *obj, FILE *out)
Saves the given object as a .plg file.
void set_loadplg_depthsort(type)
Sets the depth-sorting type to a particular value (see description of
depth sorting earlier in this document for details).
void set_loadplg_colormap(unsigned *map, int msize)
If the top bit of a color value in a .plg file is set, the bottom
15 bits are taken to be an index into a "map" that specifies the
actual color value to be used. The map consists of msize values,
each of which is an unsigned 16-bit number.
In addition, a global int variable called load_err is set to a non-zero
value if an error was encountered while loading a .plg file; see the code in
plg.c for details.
SECTION P -- FIG FILE I/O ROUTINES
A figure file consists of a set of segments. See figure.doc for details.
The format of a figure file is not tightly coupled to the rest of the
renderer.
SEGMENT *readseg(FILE *in, SEGMENT *parent)
Allocates a new segment, with the given parent, and reads a segment
description into that segment. If the file contains nested segments,
readseg will recursively call itself to load them. See figure.doc for
more information. The global integer variable readseg_err will be set
to a non-zero value if an error was encountered during loading.
void set_readseg_objlist(OBJLIST *objlist)
Sets the given object list as the one to add newly-loaded representations
to.
void set_readseg_seglist(SEGMENT **seglist, int maxsegs)
Segments in .FIG files now have an optional "segnum" attribute that
associates a simple integer with a the segment. This allows you to
subsequently refer to segments by number. The given seglist is an
array of pointers to segments, which will get filled in as segments
get loaded. The value maxsegs specifies the number of slots in
the seglist. Note that you can have as many seglists as you like,
possibly even one for each figure.
writeseg(FILE *out, SEGMENT *s, int level)
Writes the given segment out to a file; 'level' is used to provide
levels of indentation, since writeseg calls itself recursively. In
general, root segments should be written out with level = 0.
In addition, a global int variable called readseg_err is set to a non-zero
value if an error was encountered while loading a .fig file; see the code in
segio.c for details.
SECTION Q -- PCX FILE I/O ROUTINES
These routines are only available in 256 color mode, and allow you to read
and write .PCX files (PC-Paintbrush format).
load_pcx(FILE *in, int page)
Load a 320x200x256 .PCX image from the given file onto the given
display page.
int save_pcx(FILE *out, int page)
Save a 320x200x256 .PCX image into the given file from the given
display page.
SECTION R -- STEREOSCOPIC RENDERING
The rend386 package now supports stereoscopic rendering. The structure
STEREO is used to store information about the stereo environment.
The fields in this structure are:
phys_screen_dist - the distance in mm from the eye to the screen
phys_screen_width - the viewport width on the screen in mm
pixel_width - the viewport width in pixels
phys_eye_spacing - the physical spacing between the eyes, in mm
world_scaling - world units per physical mm
phys_convergence - the convergence distance in mm (usually the same
as phys_screen_dist)
All units are stored as long integers.
The following routines support stereoscopic rendering:
void make_stereo_view(VIEW *orig, VIEW *new, STEREO *s, int eye)
Creates a new VIEW from the original, using the information in s.
The 'eye' parameter should be 1 for the left eye or two for the right
eye. This function would get called twice per rendering, once with
eye == 1 and once with eye == 2.
The correct sequence to do things when updating the screen is:
fast_view_factors(&v);
make_stereo_view(&v, &left, st, 1);
render(objlist, &left);
make_stereo_view(&v, &right, st, 2);
render(objlist, &right)
void mirr_stereo_view(VIEW *orig, VIEW *new, STEREO *s, int eye)
Same as make_stereo_view(), but for use with mirror viewing instead
of Sega glasses.
void update_stereo_view(VIEW *v, STEREO *s, int eye)
If only the view has changed, you can call udpate_stereo_view() for
each eye. In other words, update_stereo_view(&left, st, 1) and
update_stereo_view(&right, st, 2).
See the routine refresh_display() in demo3.c for an example of how all this
works.
SECTION S -- SUPPORT FOR THE SEGA 3D GLASSES AND NINTENDO POWERGLOVE
The Sega 3D glasses are now supported by rend386. See the file sega.txt for
details on how to build an interface for the Sega glasses that allows them to
be connected to a serial port.
The Nintendo Powerglove is now supported by rend386. See the article in the
June 1990 Byte magazine for details on constructing a cable that allows the
glove to be connected to a parallel port.
In order to use the Sega glasses or the Nintendo Powerglove, you must call
the following routine:
void init_SG_interrupt(void (*sega_switcher), void (*glove_handler), int tc)
Installs interrupt support for the Sega glasses and the Nintendo glove.
The sega_switcher() routine will be called just before the vertical
retrace triggers, and the glove_handler() routine will be called at
least once per screen refresh. If either routine is NULL, then that
routine will not be called. The tc value indicates the interval (in
units of 1.19 microseconds) for polling the glove; 6500 is a good
value to use if the sega_switcher is also being used.
Two routines are provided which may be passed as parameters to the
init_SG_interrupt() routine: switch_sega() and glove_int_handler().
The switch_sega() routine switches the sega glasses from left eye to right
eye or vice-versa, and swaps the screen as well. The global variable
left_page is the number of the screen page that contains the left-eye image,
and right_page is the number of the screen page that contains the right-eye
image. In Mode X, four pages are available altogether, so you can have
an "active" left-right pair and a "visual" left-right pair; see the code
for refresh_display in demo3.c for details on how this works.
The global variable rs232_port_adr contains the address of the serial port
that should be used for controlling the glasses.
The routine sega_off() can be used to make both lenses on the Sega glasses
transparent; it should be called before exiting to avoid damaging the glasses.
The following struct contains data from the Powerglove:
typedef struct {
signed char x, y, z, rot, fingers, keys, gstat1, gstat2, rxflags;
unsigned int nmissed;
} glove_data;
The x, y, and z values are the location of the glove in three-space; the
rot value is the rotation position (0 to 12). The fingers are a one-byte
bitmap, two bits for each of the thumb and three fingers (pinky not included).
The keys value indicates which keys are down on the wrist control panel.
The following routines can be used to interface to the Powerglove:
void glove_init(int gdeg)
Sets up the glove and enters hi-res mode. If gdeg is non-zero, deglitching
is enabled.
int glove_ready()
Returns 0 if the glove is not ready, 1 if data is ready, 2 if data is ready
and the rxflags are valid.
int glove_read(glove_data *gdata)
Reads the glove data into the given struct, and returns non-zero if the
data has changed since the last time the routine was called.
Note that there is still considerable inherent glitching in the glove due
to noise, reflections and so forth.
THE FUTURE:
This rendering package is intended to be the foundation for a wide range
of applications.
For further information about the package, feel free to contact us:
Bernie Roehl (broehl@sunee.waterloo.edu)
Dave Stampe (dstampe@sunee.waterloo.edu)
Note that at some point in the future, sunee.waterloo.edu will become
sunee.uwaterloo.ca (the ".ca" stands for CAnada, not CAlifornia!)
The major contribution others can make to the project at this stage is
to write converters from other polygon formats to OFF. In particular,
a DXF to OFF converter would let us create objects with a CAD package.
OFF is a reasonably good, open format that encodes author/copyright
information along with geometry and colors. While it's a bit of a
nuisance to parse, it's very easy to generate.
Two of our data files were converted from OFF files using off2plg: the bishop
and the banana. Below is the author/copyright information from their .aoff
files:
name bishop8
description chess piece - bishop
author Randy Brown, brown@cs.unc.edu
copyright (c) Randy Brown, OK to distribute if copyright/author appears
name banana
description Banana made on Frank Crow's U. Utah surface design system
copyright (c) Ohio State Univ. - ok to distribute if copyright appears
That's it. Please direct any questions to broehl@sunee.waterloo.edu or
dstamp@serv1.waterloo.edu, or join the mailing list. To join the list,
send mail to rend386-request@sunee.uwaterloo.ca (the list itself is just
rend386@sunee.uwaterloo.ca).